package service

import (
	"context"
	"errors"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"gorm.io/gorm"
	"net/http"
	"net/url"
	"strings"
	"time"
	"ylm.sl/common/constant"
	"ylm.sl/common/enums/VailDateTypeEnum"
	"ylm.sl/common/utils"
	"ylm.sl/common/utils/bloomfilter/hash_util"
	"ylm.sl/global"
	"ylm.sl/internal/api/request"
	"ylm.sl/internal/api/response"
	"ylm.sl/internal/model"
	"ylm.sl/internal/repository/dao"
)

type IShortLinkService interface {
	CreateShortLink(context *gin.Context, param *request.ShortLinkCreateReqDTO) (*response.ShortLinkCreateRespDTO, error)
	CreateShortLinkByLock(context *gin.Context, r *request.ShortLinkCreateReqDTO) (*response.ShortLinkCreateRespDTO, error)
	BatchCreateShortLink(c *gin.Context, r *request.ShortLinkBatchCreateReqDTO) (*response.ShortLinkBatchCreateRespDTO, error)
	UpdateShortLink(c *gin.Context, r *request.ShortLinkUpdateReqDTO) error
}
type ShortLinkServiceImpl struct {
	repo                                      *dao.ShortLinkDao
	shortUriCreateCachePenetrationBloomFilter *utils.BloomFilter
	createShortLinkDefaultDomain              string
}

func (s *ShortLinkServiceImpl) UpdateShortLink(context *gin.Context, requestParam *request.ShortLinkUpdateReqDTO) error {
	//TODO verificationWhitelist
	hasShortLinkDO, err := s.repo.SelectOne(context, requestParam.OriginUrl, requestParam.FullShortUrl, 0)
	if err != nil || hasShortLinkDO == nil {
		return errors.New("short-link not found")
	}

	hasShortLinkDO.Gid = requestParam.Gid
	hasShortLinkDO.OriginUrl = requestParam.OriginUrl
	hasShortLinkDO.FullShortUrl = requestParam.FullShortUrl
	if requestParam.OriginUrl != hasShortLinkDO.OriginUrl {
		hasShortLinkDO.Favicon = s.getFavicon(requestParam.OriginUrl)
	}
	hasShortLinkDO.Describe = requestParam.Describe
	if err = s.repo.Update(context, hasShortLinkDO); err != nil {
		return err
	}
	if hasShortLinkDO.ValidDateType != requestParam.ValidDateType ||
		!hasShortLinkDO.ValidDate.Equal(time.Time(requestParam.ValidDate)) ||
		hasShortLinkDO.OriginUrl != requestParam.OriginUrl {
		global.Redis.Del(context, fmt.Sprintf(constant.GOTO_SHORT_LINK_KEY, requestParam.FullShortUrl))
		if hasShortLinkDO.ValidDate.Before(time.Now()) {
			if requestParam.ValidDateType == VailDateTypeEnum.PERMANENT ||
				time.Time(requestParam.ValidDate).After(time.Now()) {

			}
		}
	}
	return err
}

func (s *ShortLinkServiceImpl) BatchCreateShortLink(context *gin.Context, requestParam *request.ShortLinkBatchCreateReqDTO) (*response.ShortLinkBatchCreateRespDTO, error) {
	originUrls := requestParam.OriginUrls
	describes := requestParam.Describes
	result := []response.ShortLinkBaseInfoRespDTO{}
	for i, originUrl := range originUrls {
		shortLinkCreateReqDTO := request.ShortLinkCreateReqDTO{
			OriginUrl:     originUrl,
			Gid:           requestParam.Gid,
			CreatedType:   requestParam.CreatedType,
			ValidDateType: requestParam.ValidDateType,
			ValidDate:     request.Date{},
			Describe:      describes[uint32(i)],
		}
		shortLink, err := s.CreateShortLink(context, &shortLinkCreateReqDTO)
		if err != nil {
			return nil, err
		}
		linkBaseInfoRespDTO := response.ShortLinkBaseInfoRespDTO{
			FullShortUrl: shortLink.FullShortUrl,
			OriginUrl:    shortLink.OriginUrl,
			Describe:     shortLinkCreateReqDTO.Describe,
		}
		result = append(result, linkBaseInfoRespDTO)
	}
	return &response.ShortLinkBatchCreateRespDTO{
		Total:         len(result),
		BaseLinkInfos: result,
	}, nil
}

func (s *ShortLinkServiceImpl) CreateShortLinkByLock(context *gin.Context, requestParam *request.ShortLinkCreateReqDTO) (*response.ShortLinkCreateRespDTO, error) {
	var err error
	//TODO verificationWhitelist
	var fullShortUrl string
	lock := global.RedSync.NewMutex(constant.SHORT_LINK_CREATE_LOCK_KEY)
	lock.Lock()
	defer lock.Unlock()
	shortLinkSuffix := s.generateSuffix(context, requestParam)
	fullShortUrl = s.createShortLinkDefaultDomain + "/" + shortLinkSuffix
	shortlink := &model.ShortLink{
		Domain:       s.createShortLinkDefaultDomain,
		OriginUrl:    requestParam.OriginUrl,
		Gid:          requestParam.Gid,
		CreatedType:  requestParam.CreatedType,
		ValidDate:    time.Time(requestParam.ValidDate),
		Describe:     requestParam.Describe,
		ShortUri:     shortLinkSuffix,
		EnableStatus: 0,
		TodayPv:      0,
		TodayUip:     0,
		TodayUv:      0,
		DelTime:      0,
		FullShortUrl: fullShortUrl,
		Favicon:      s.getFavicon(requestParam.OriginUrl),
	}
	err = s.repo.Insert(context, shortlink)
	if errors.Is(err, gorm.ErrDuplicatedKey) {
		return nil, errors.New(fmt.Sprintf("短链接：%s 生成重复", fullShortUrl))
	}
	global.Redis.Set(context, fmt.Sprintf(constant.GOTO_SHORT_LINK_KEY, fullShortUrl), requestParam.OriginUrl, time.Time(requestParam.ValidDate).Sub(time.Now()))
	return &response.ShortLinkCreateRespDTO{
		FullShortUrl: fullShortUrl,
		OriginUrl:    requestParam.OriginUrl,
		Gid:          requestParam.Gid,
	}, err
}

func (s *ShortLinkServiceImpl) CreateShortLink(context *gin.Context, requestParam *request.ShortLinkCreateReqDTO) (*response.ShortLinkCreateRespDTO, error) {
	var err error
	//TODO verificationWhitelist
	shortLinkSuffix := s.generateSuffix(context, requestParam)
	fullShortUrl := s.createShortLinkDefaultDomain + "/" + shortLinkSuffix
	shortlink := &model.ShortLink{
		Domain:       s.createShortLinkDefaultDomain,
		OriginUrl:    requestParam.OriginUrl,
		Gid:          requestParam.Gid,
		CreatedType:  requestParam.CreatedType,
		ValidDate:    time.Time(requestParam.ValidDate),
		Describe:     requestParam.Describe,
		ShortUri:     shortLinkSuffix,
		EnableStatus: 0,
		TodayPv:      0,
		TodayUip:     0,
		TodayUv:      0,
		DelTime:      0,
		FullShortUrl: fullShortUrl,
		Favicon:      s.getFavicon(requestParam.OriginUrl),
	}
	err = s.repo.Insert(context, shortlink)
	if errors.Is(err, gorm.ErrDuplicatedKey) {
		// 首先判断是否存在布隆过滤器，如果不存在直接新增
		if !s.shortUriCreateCachePenetrationBloomFilter.Contains(context, requestParam.OriginUrl) {
			s.shortUriCreateCachePenetrationBloomFilter.Add(context, requestParam.OriginUrl)
		}
		return nil, errors.New(fmt.Sprintf("短链接：%s 生成重复", fullShortUrl))
	}
	global.Redis.Set(context, fmt.Sprintf(constant.GOTO_SHORT_LINK_KEY, fullShortUrl), requestParam.OriginUrl, time.Time(requestParam.ValidDate).Sub(time.Now()))
	s.shortUriCreateCachePenetrationBloomFilter.Add(context, fullShortUrl)
	return &response.ShortLinkCreateRespDTO{
		FullShortUrl: fullShortUrl,
		OriginUrl:    requestParam.OriginUrl,
		Gid:          requestParam.Gid,
	}, err
}

func (s *ShortLinkServiceImpl) generateSuffix(c context.Context, param *request.ShortLinkCreateReqDTO) string {
	shorUri := ""
	for customGenerateCount := 0; customGenerateCount < 10; customGenerateCount++ {
		originUrl := param.OriginUrl
		originUrl += uuid.New().String()
		shorUri = hash_util.HashToBase62(originUrl)
		if !s.shortUriCreateCachePenetrationBloomFilter.Contains(c, s.createShortLinkDefaultDomain+"/"+shorUri) {
			break
		}
	}
	return shorUri
}

func (s *ShortLinkServiceImpl) getFavicon(targetURL string) string {
	// 1. 验证并解析URL
	parsedURL, err := url.Parse(targetURL)
	if err != nil {
		return ""
	}

	// 2. 发送HTTP请求
	resp, err := http.Get(targetURL)
	if err != nil {
		return ""
	}
	defer resp.Body.Close()

	// 3. 检查HTTP状态码
	if resp.StatusCode != http.StatusOK {
		return ""
	}

	// 4. 解析HTML文档
	doc, err := goquery.NewDocumentFromReader(resp.Body)
	if err != nil {
		return ""
	}

	// 5. 查找favicon链接（支持多种rel属性）
	faviconSelectors := []string{
		"link[rel='shortcut icon']",
		"link[rel='icon']",
		"link[rel='SHORTCUT ICON']",
	}

	var faviconURL string
	for _, selector := range faviconSelectors {
		doc.Find(selector).Each(func(i int, s *goquery.Selection) {
			if href, exists := s.Attr("href"); exists && faviconURL == "" {
				faviconURL = href
			}
		})
		if faviconURL != "" {
			break
		}
	}

	// 6. 处理找到的favicon URL
	if faviconURL != "" {
		// 转换为绝对URL
		return s.toAbsoluteURL(parsedURL, faviconURL)
	}

	// 7. 未找到时使用默认路径
	return s.toAbsoluteURL(parsedURL, "/favicon.ico")
}

func (s *ShortLinkServiceImpl) toAbsoluteURL(base *url.URL, path string) string {
	if strings.HasPrefix(path, "http") {
		return path // 已经是绝对URL
	}

	if strings.HasPrefix(path, "//") {
		return base.Scheme + ":" + path // 协议相对URL
	}

	// 解析为绝对路径
	absURL, err := base.Parse(path)
	if err != nil {
		return "" // 解析失败
	}
	return absURL.String()
}

func NewShortLinkService(repo *dao.ShortLinkDao, shortUriCreateCachePenetrationBloomFilter *utils.BloomFilter) IShortLinkService {
	return &ShortLinkServiceImpl{
		repo: repo,
		shortUriCreateCachePenetrationBloomFilter: shortUriCreateCachePenetrationBloomFilter,
		createShortLinkDefaultDomain:              global.Config.ShortLink.Domain.Default,
	}
}
